﻿using System;
using System.IO;
using System.Text;
using System.Xml;

//自定义命名空间
using DreamCube.Foundation.WMI;
using DreamCube.Foundation.Security;
using DreamCube.Foundation.Basic.Utility;

namespace DreamCube.Framework.Utilities.Authorization
{
    /// <summary>
    /// 授权的基类
    /// </summary>
    public abstract class AuthoriseBasic
    {
        #region "字段"

        /// <summary>
        /// 标志是否验证过了
        /// </summary>
        private Boolean isAuthorise = false;

        /// <summary>
        /// 安全性方面的操作
        /// </summary>
        private ISecurity securityObj;

        /// <summary>
        /// 授权剩余天数
        /// </summary>
        private Double remainingDays = -1;

        /// <summary>
        /// 额外数据
        /// </summary>
        private Byte[] extraData;

        #endregion

        #region "属性"

        /// <summary>
        /// 获取额外数据的方法
        /// </summary>
        /// <returns></returns>
        public Byte[] ExtraData
        {
            get
            {
                if (!isAuthorise)
                    throw new Exception(String.Format(Properties.Resources.ExceptionNotAuthorise, "ExtraData"));
                return this.extraData;
            }
        }

        /// <summary>
        /// 获取剩余的使用天数
        /// </summary>
        /// <returns></returns>
        public Double RemainingDays
        {
            get 
            {
                if (!isAuthorise)
                    throw new Exception(String.Format(Properties.Resources.ExceptionNotAuthorise, "RemainingDays"));
                return remainingDays; 
            }
        }

        /// <summary>
        /// 返回当前的授权类型
        /// </summary>
        public abstract AuthoriseType AuthoriseType
        {
            get;
        }

        #endregion

        #region "公共方法"

        public AuthoriseBasic(ISecurity securityObj)
        {
            this.securityObj = securityObj;
        }

        /// <summary>
        /// 创建文件名（不包括扩展名）
        /// DC_[0-9]_[1-8]{2}_[B-Q]{3}_[a-m]{4}
        /// </summary>
        /// <returns></returns>
        public String CreateFileNameWithoutExtensionName()
        {
            Random rand = new Random();
            String name = "DC_{0}_{1}_{2}_{3}";
            String s1 = rand.Next(0, 9).ToString();
            String s2 = rand.Next(1, 8).ToString() + rand.Next(1, 8).ToString();
            String s3 = MyInt32.ToASCII(rand.Next(66, 81)) + MyInt32.ToASCII(rand.Next(66, 81)) + MyInt32.ToASCII(rand.Next(66, 81));
            String s4 = MyInt32.ToASCII(rand.Next(97, 109)) + MyInt32.ToASCII(rand.Next(97, 109)) + MyInt32.ToASCII(rand.Next(97, 109)) + MyInt32.ToASCII(rand.Next(97, 109));
            return String.Format(name, s1, s2, s3, s4);
        }

        /// <summary>
        /// 创建授权文件
        /// </summary>
        /// <param name="outputFilePath"></param>
        /// <param name="data0FileData">等待加密的数据</param>
        /// <param name="startTimeAndEndTime">
        /// 数组长度必须等于2
        /// 第一位日期表示授权使用的开始日期（用于判断用户更改系统日期而继续使用软件的情况）;
        /// 第二位日期表示授权使用的结束日期
        /// </param>
        /// <param name="extraData">额外数据</param>
        public Boolean CreateAuthoriseFile(String outputFilePath, 
                                        Byte[] data0FileData,
                                        DateTime[] startTimeAndEndTime = null,
                                        Byte[] extraData = null)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml("<Authorization></Authorization>");

            #region "载入授权类型"

            XmlNode authoriseTypeNode = doc.CreateNode(XmlNodeType.Element, "AuthoriseType", "");
            authoriseTypeNode.InnerText = this.AuthoriseType.ToString();
            doc.DocumentElement.AppendChild(authoriseTypeNode);

            #endregion

            #region "载入授权期限"

            if (startTimeAndEndTime != null)
            {
                //长度不为2，抛出异常
                if (startTimeAndEndTime.Length != 2 || (startTimeAndEndTime[1] - startTimeAndEndTime[0]).TotalDays < 1)
                    throw new ArgumentException(Properties.Resources.ExceptionAuthorizeTimes);

                if ((startTimeAndEndTime[1] - System.DateTime.Now).TotalDays < 1)
                    throw new ArgumentException(String.Format(Properties.Resources.ExceptionAuthorizeEndTime, startTimeAndEndTime[1]));

                XmlNode dateTimeLengthNode = doc.CreateNode(XmlNodeType.Element, "DateTimeLength", "");
                XmlNode startTimeNode = doc.CreateNode(XmlNodeType.Element, "StartTime", "");
                startTimeNode.InnerText = startTimeAndEndTime[0].ToString("yyyy-MM-dd");
                dateTimeLengthNode.AppendChild(startTimeNode);

                XmlNode endTimeNode = doc.CreateNode(XmlNodeType.Element, "EndTime", "");
                endTimeNode.InnerText = startTimeAndEndTime[1].ToString("yyyy-MM-dd");
                dateTimeLengthNode.AppendChild(endTimeNode);

                //把授权时长加到XML文档中
                doc.DocumentElement.AppendChild(dateTimeLengthNode);
            }

            #endregion

            #region "载入Data0文件"

            XmlNode data0FileDataNode = doc.CreateNode(XmlNodeType.Element, "Data0FileData", "");
            data0FileDataNode.InnerText = Convert.ToBase64String(data0FileData);
            doc.DocumentElement.AppendChild(data0FileDataNode);

            #endregion

            #region "载入额外的授权的信息数据"

            if (extraData != null)
            {
                XmlNode authoriseDataNode = doc.CreateNode(XmlNodeType.Element, "AuthoriseExtraData", "");
                authoriseDataNode.InnerText = Convert.ToBase64String(extraData);
                doc.DocumentElement.AppendChild(authoriseDataNode);
            }

            #endregion

            using (MemoryStream ms = new MemoryStream())
            {
                doc.Save(ms);
                Byte[] encryptData = securityObj.Encrypt(ms.ToArray());
                MyIO.Write(outputFilePath, encryptData);
            }
            return true;
        }

        /// <summary>
        /// 创建data0数据文件
        /// </summary>
        /// <param name="outputFilePath"></param>
        public void CreateData0FileData(String outputFilePath)
        {
            MyIO.Write(outputFilePath, GenerateData0FileData());
        }

        /// <summary>
        /// 验证授权；返回验证结果
        /// </summary>
        /// <param name="authoriseFilePath"></param>
        /// <returns></returns>
        public AuthorizeResult Authorise(String authoriseFilePath)
        {
            if (this.isAuthorise) return new AuthorizeResult(true, "");

            Byte[] data = MyIO.ReadByte(authoriseFilePath);

            #region "解密数据"

            Byte[] decryptData = null;
            try
            {
                decryptData = securityObj.Decrypt(data);
            }
            catch (Exception)
            {
                return new AuthorizeResult(false, Properties.Resources.FailValidateAuthoriseFile);
            }

            #endregion

            #region "还原成XML文档"

            XmlDocument doc = new XmlDocument();
            try
            {
                doc.Load(new MemoryStream(decryptData));
            }
            catch (Exception)
            {
                return new AuthorizeResult(false, Properties.Resources.FailValidateAuthoriseFile);
            }

            #endregion

            #region "验证日期"

            XmlNode authoriseTypeNode = doc.SelectSingleNode("/Authorization/AuthoriseType");
            if (authoriseTypeNode != null)
            {
                AuthoriseType type;
                if (Enum.TryParse<AuthoriseType>(authoriseTypeNode.InnerText, out type))
                {
                    //需要验证日期
                    if (type == Authorization.AuthoriseType.IntervalWithHardware ||
                        type == Authorization.AuthoriseType.IntervalWithoutHardware)
                    {
                        XmlNode dateTimeLengthNode = doc.SelectSingleNode("/Authorization/DateTimeLength");
                        if (dateTimeLengthNode != null)
                        {
                            //验证开始时间
                            XmlNode startTimeNode = dateTimeLengthNode.SelectSingleNode("StartTime");
                            if (startTimeNode == null)
                                return new AuthorizeResult(false, Properties.Resources.FailValidateAuthoriseFile);
                            if (System.DateTime.Now < MyString.ToDateTime(startTimeNode.InnerText))
                                return new AuthorizeResult(false, Properties.Resources.FailValidateAuthoriseFile);
                            //验证结束时间
                            XmlNode endTimeNode = dateTimeLengthNode.SelectSingleNode("EndTime");
                            if (endTimeNode == null)
                                return new AuthorizeResult(false, Properties.Resources.FailValidateAuthoriseFile);
                            DateTime endTime = MyString.ToDateTime(endTimeNode.InnerText);
                            if (System.DateTime.Now > endTime)
                                return new AuthorizeResult(false, Properties.Resources.FailValidateAuthoriseFile);

                            remainingDays = (System.DateTime.Now - endTime).TotalDays;
                        }
                    }
                    else remainingDays = Double.MaxValue;  //剩余天数为最大值
                } 
            }

            #endregion

            #region ‘验证data0文件数据"

            AuthorizeResult result = null;
            XmlNode data0FileDataNode = doc.SelectSingleNode("/Authorization/Data0FileData");
            if (data0FileDataNode != null)
                result = AuthorizeData0FileData(Convert.FromBase64String(data0FileDataNode.InnerText));
            else return new AuthorizeResult(false, Properties.Resources.FailValidateData1);

            if (!result.IsPass)
                return result;

            #endregion

            //授权的信息数据
            XmlNode extraDataNode = doc.SelectSingleNode("/Authorization/ExtraData");
            if (extraDataNode != null)
                this.extraData = Convert.FromBase64String(extraDataNode.InnerText);
            else 
                this.extraData = null;

            this.isAuthorise = true;
            return result;
        }

        #endregion

        #region "受保护方法"

        /// <summary>
        /// 验证data0文件数据
        /// </summary>
        /// <returns></returns>
        protected virtual AuthorizeResult AuthorizeData0FileData(Byte[] data)
        {
            if (MyByte.EqualEx(data, this.GenerateData0FileData()))
                return new AuthorizeResult(true, "");
            return new AuthorizeResult(false, Properties.Resources.FailValidateData1);
        }

        /// <summary>
        /// 创建Data0文件数据
        /// </summary>
        /// <returns></returns>
        protected abstract Byte[] GenerateData0FileData();

        #endregion

    }
}
